#pragma once

#include <algorithm>
#include <cctype>
#include <ios>
#include <string>
#include <unordered_map>
#include <vector>

#include "arithmetic.h"
#include "ast.h"
#include "semantics.h"
#include "types.h"
#include "utils.h"

struct ExtFunction {
    std::string name;
    Type::Basic retType;
    std::vector<Type> argType;
    bool hasVA;

    ExtFunction(const std::string& name, char ret, const std::string& arg);
};

struct Symbol {
    std::string name;
    Semantics::Info* info = nullptr;
    bool isGlobal = false;
    int allocMem = 0;

    static Symbol* CreateConstant(const std::string& name, ArithValue value);
    static Symbol* CreateArray(Type::Basic type, const std::string& name,
                               const std::vector<int>& shape, int length);
    static Symbol* CreateLValue(Type::Basic type, const std::string& name);
    static Symbol* CreateFunction(Type::Basic type, const std::string& name,
                                  AST::ASTNode* func);
};

class SymbolMap {
  public:
    enum class Context {
        None,
        Global,
        Argument,
        Local,
    };

  private:
    int depth = 0;
    std::vector<Context> context;
    std::unordered_map<std::string, std::vector<std::pair<int, Symbol*>>> syms;

  public:
    std::unordered_map<std::string, Symbol*> ext;
    std::unordered_map<std::string, Symbol*> global;
    std::vector<std::string> order;

    SymbolMap() { context.push_back(Context::None); }
    Context GetContext() { return context.back(); }
    void Add(Symbol* sym);
    Symbol* Find(const std::string& name);

    void CreateEnv(Context c);
    void ExitEnv();

    void PrintGlobal();
};
